﻿using System;
using ConsoleApplication18;

namespace ConsoleApplication15
{
    delegate Tuple<T, Chain<T>> Chain<T>();

    static class ChainExtensions
    {
        public static Chain<TResult> Chain<T, TResult>(this T arg, Func<T, Tuple<TResult, T>> func)
        {
            return () => {
                var tuple = func(arg);
                return Tuple.Create(tuple.Item1, Chain(tuple.Item2, func));
            };
        }
    }

    class Program
    {
        // ReSharper disable UnusedMember.Local
        static void Main()
        // ReSharper restore UnusedMember.Local
        {
            ConsoleApplication19.Program3.Main2();
            //Console.WriteLine(estimate_pi(1000));
            //ConsoleApplication16.EnumerableProgram.Main2();
            //ConsoleApplication16.FactorialProgram.Main2();
            //var chain = enumerate_interval(0, 10);
            //while (chain().Item1.HasValue)
            //{
            //    Console.WriteLine(chain().Item1.Value);
            //    chain = chain().Item2;
            //}
        }

        static Chain<Option<int>> enumerate_interval(int low, int high)
        {
            return low.Chain(x => Tuple.Create(x <= high ? x : Option<int>.None, x + 1));
        }

        static double estimate_pi(int trials)
        {
            return Math.Sqrt(6 / random_gcd_test(
                trials,
                random_init.Chain(x => Tuple.Create(x, rand_update(x))).Chain(rand => {
                    var tuple1 = rand();
                    var tuple2 = tuple1.Item2();
                    return Tuple.Create(gcd(tuple1.Item1, tuple2.Item1) == 1, tuple2.Item2);
                })));
        }

        const int random_init = 0;

        static double random_gcd_test(int trials, Chain<bool> experiment)
        {
            return random_gcd_test_iter(trials, experiment, trials, 0);
        }

        static double random_gcd_test_iter(int trials, Chain<bool> experiment,
                                           int trials_remaining, int trials_passed)
        {
            if (trials_remaining == 0)
                return (double)trials_passed / trials;
            var tuple = experiment();
            if (tuple.Item1)
                return random_gcd_test_iter(trials, tuple.Item2, trials_remaining - 1, trials_passed + 1);
            else
                return random_gcd_test_iter(trials, tuple.Item2, trials_remaining - 1, trials_passed);
        }

        private static int rand_update(int i)
        {
            return random.Next();
        }

        static int gcd(int a, int b)
        {
            while (b != 0)
                b = a % (a = b);
            return a;
        }

        static readonly Random random = new Random();
    }
}
